{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Requirements Generator and MoSCoW Prioritization\n",
    "**Author:** Gael Sánchez\n",
    "**LinkedIn:** www.linkedin.com/in/gaelsanchez\n",
    "\n",
    "This notebook generates and validates functional and non-functional software requirements from a natural language description, and classifies them using the MoSCoW prioritization technique.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What is a MoSCoW Matrix?\n",
    "\n",
    "The MoSCoW Matrix is a prioritization technique used in software development to categorize requirements based on their importance and urgency. The acronym stands for:\n",
    "\n",
    "- **Must Have** – Critical requirements that are essential for the system to function.  \n",
    "- **Should Have** – Important requirements that add significant value, but are not critical for initial delivery.  \n",
    "- **Could Have** – Nice-to-have features that can enhance the product, but are not necessary.  \n",
    "- **Won’t Have (for now)** – Low-priority features that will not be implemented in the current scope.\n",
    "\n",
    "This method helps development teams make clear decisions about what to focus on, especially when working with limited time or resources. It ensures that the most valuable and necessary features are delivered first, contributing to better project planning and stakeholder alignment.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How it works\n",
    "\n",
    "This notebook uses the OpenAI library (via the Gemini API) to extract and validate software requirements from a natural language description. The workflow follows these steps:\n",
    "\n",
    "1. **Initial Validation**  \n",
    "   The user provides a textual description of the software. The model evaluates whether the description contains enough information to derive meaningful requirements. Specifically, it checks if the description answers key questions such as:\n",
    "   \n",
    "   - What is the purpose of the software?  \n",
    "   - Who are the intended users?  \n",
    "   - What are the main features and functionalities?  \n",
    "   - What platform(s) will it run on?  \n",
    "   - How will data be stored or persisted?  \n",
    "   - Is authentication/authorization needed?  \n",
    "   - What technologies or frameworks will be used?  \n",
    "   - What are the performance expectations?  \n",
    "   - Are there UI/UX principles to follow?  \n",
    "   - Are there external integrations or dependencies?  \n",
    "   - Will it support offline usage?  \n",
    "   - Are advanced features planned?  \n",
    "   - Are there security or privacy concerns?  \n",
    "   - Are there any constraints or limitations?  \n",
    "   - What is the timeline or development roadmap?\n",
    "\n",
    "   If the description lacks important details, the model requests the missing information from the user. This loop continues until the model considers the description complete.\n",
    "\n",
    "2. **Summarization**  \n",
    "   Once validated, the model summarizes the software description, extracting its key aspects to form a concise and informative overview.\n",
    "\n",
    "3. **Requirements Generation**  \n",
    "   Using the summary, the model generates a list of functional and non-functional requirements.\n",
    "\n",
    "4. **Requirements Validation**  \n",
    "   A separate validation step checks if the generated requirements are complete and accurate based on the summary. If not, the model provides feedback, and the requirements are regenerated accordingly. This cycle repeats until the validation step approves the list.\n",
    "\n",
    "5. **MoSCoW Prioritization**  \n",
    "   Finally, the validated list of requirements is classified using the MoSCoW prioritization technique, grouping them into:\n",
    "   \n",
    "   - Must have  \n",
    "   - Should have  \n",
    "   - Could have  \n",
    "   - Won't have for now\n",
    "\n",
    "The output is a clear, structured requirements matrix ready for use in software development planning.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example Usage\n",
    "\n",
    "### Input\n",
    "\n",
    "**Software Name:** Personal Task Manager  \n",
    "**Initial Description:**  \n",
    "This will be a simple desktop application that allows users to create, edit, mark as completed, and delete daily tasks. Each task will have a title, an optional description, a due date, and a status (pending or completed). The goal is to help users organize their activities efficiently, with an intuitive and minimalist interface.\n",
    "\n",
    "**Main Features:**\n",
    "\n",
    "- Add new tasks  \n",
    "- Edit existing tasks  \n",
    "- Mark tasks as completed  \n",
    "- Delete tasks  \n",
    "- Filter tasks by status or date\n",
    "\n",
    "**Additional Context Provided After Model Request:**\n",
    "\n",
    "- **Intended Users:** Individuals seeking to improve their daily productivity, such as students, remote workers, and freelancers.  \n",
    "- **Platform:** Desktop application for common operating systems.  \n",
    "- **Data Storage:** Tasks will be stored locally.  \n",
    "- **Authentication/Authorization:** A lightweight authentication layer may be included for data protection.  \n",
    "- **Technology Stack:** Cross-platform technologies that support a modern, functional UI.  \n",
    "- **Performance:** Expected to run smoothly with a reasonable number of active and completed tasks.  \n",
    "- **UI/UX:** Prioritizes a simple, modern user experience.  \n",
    "- **Integrations:** Future integration with calendar services is considered.  \n",
    "- **Offline Usage:** The application will work without an internet connection.  \n",
    "- **Advanced Features:** Additional features like notifications or recurring tasks may be added in future versions.  \n",
    "- **Security/Privacy:** User data privacy will be respected and protected.  \n",
    "- **Constraints:** Focus on simplicity, excluding complex features in the initial version.  \n",
    "- **Timeline:** Development planned in phases, starting with a functional MVP.\n",
    "\n",
    "### Output\n",
    "\n",
    "**MoSCoW Prioritization Matrix:**\n",
    "\n",
    "**Must Have**\n",
    "- Task Creation: [The system needs to allow users to add tasks to be functional.]  \n",
    "- Task Editing: [Users must be able to edit tasks to correct mistakes or update information.]  \n",
    "- Task Completion: [Marking tasks as complete is a core function of a task management system.]  \n",
    "- Task Deletion: [Users need to be able to remove tasks that are no longer relevant.]  \n",
    "- Task Status: [Maintaining task status (pending/completed) is essential for tracking progress.]  \n",
    "- Data Persistence: [Tasks must be stored to be useful beyond a single session.]  \n",
    "- Performance: [The system needs to perform acceptably for a reasonable number of tasks.]  \n",
    "- Usability: [The system must be easy to use for all other functionalities to be useful.]\n",
    "\n",
    "**Should Have**\n",
    "- Task Filtering by Status: [Filtering enhances usability and allows users to focus on specific tasks.]  \n",
    "- Task Filtering by Date: [Filtering by date helps manage deadlines.]  \n",
    "- User Interface Design: [A modern design improves user experience.]  \n",
    "- Platform Compatibility: [Running on common OSes increases adoption.]  \n",
    "- Data Privacy: [Important for user trust, can be gradually improved.]  \n",
    "- Security: [Basic protections are necessary, advanced features can wait.]\n",
    "\n",
    "**Could Have**\n",
    "- Optional Authentication: [Enhances security but adds complexity.]  \n",
    "- Offline Functionality: [Convenient, but not critical for MVP.]\n",
    "\n",
    "**Won’t Have (for now)**\n",
    "- N/A: [No features were excluded completely at this stage.]\n",
    "\n",
    "---\n",
    "\n",
    "This example demonstrates how the notebook takes a simple description and iteratively builds a complete and validated set of software requirements, ultimately organizing them into a MoSCoW matrix for development planning.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "from pydantic import BaseModel\n",
    "import gradio as gr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "load_dotenv(override=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "gemini = OpenAI(\n",
    "    api_key=os.getenv(\"GOOGLE_API_KEY\"), \n",
    "    base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
    ")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StandardSchema(BaseModel):\n",
    "    understood: bool\n",
    "    feedback: str\n",
    "    output: str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This is the prompt to validate the description of the software product on the first step\n",
    "system_prompt = f\"\"\"\n",
    "        You are a software analyst. the user will give you a description of a software product. Your task is to decide the description provided is complete and accurate and useful to derive requirements for the software.\n",
    "        If you decide the description is not complete or accurate, you should provide a kind message to the user listing the missing or incorrect information, and ask them to provide the missing information.\n",
    "        If you decide the description is complete and accurate, you should provide a summary of the description in a structured format. Only provide the summary, nothing else.\n",
    "        Ensure that the description answers the following questions:\n",
    "        - What is the purpose of the software?\n",
    "        - Who are the intended users?\n",
    "        - What are the main features and functionalities of the software?\n",
    "        - What platform(s) will it run on?\n",
    "        - How will data be stored or persisted?\n",
    "        - Is user authentication or authorization required?\n",
    "        - What technologies or frameworks will be used?\n",
    "        - What are the performance expectations?\n",
    "        - Are there any UI/UX design principles that should be followed?\n",
    "        - Are there any external integrations or dependencies?\n",
    "        - Will it support offline usage?\n",
    "        - Are there any planned advanced features?\n",
    "        - Are there any security or privacy considerations?\n",
    "        - Are there any constrains or limitations?\n",
    "        - What is the desired timeline or development roadmap?\n",
    "\n",
    "        Respond in the following format:\n",
    "        \n",
    "        \"understood\": true only if the description is complete and accurate\n",
    "        \"feedback\": Instructions to the user to provide the missing or incorrect information.\n",
    "        \"output\": Summary of the description in a structured format, once the description is complete and accurate.\n",
    "        \n",
    "    \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function is used to validate the description and provide feedback to the user.\n",
    "# It receives the messages from the user and the system prompt.\n",
    "# It returns the validation response.\n",
    "\n",
    "def validate_and_feedback(messages):\n",
    "\n",
    "    validation_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=StandardSchema)\n",
    "    validation_response = validation_response.choices[0].message.parsed\n",
    "    return validation_response\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function is used to validate the requirements and provide feedback to the model.\n",
    "# It receives the description and the requirements.\n",
    "# It returns the validation response.\n",
    "\n",
    "def validate_requirements(description, requirements):\n",
    "    validator_prompt = f\"\"\"\n",
    "        You are a software requirements reviewer.\n",
    "        Your task is to analyze a set of functional and non-functional requirements based on a given software description.\n",
    "\n",
    "        Perform the following validation steps:\n",
    "\n",
    "        Completeness: Check if all key features, fields, and goals mentioned in the description are captured as requirements.\n",
    "\n",
    "        Consistency: Verify that all listed requirements are directly supported by the description. Flag anything that was added without justification.\n",
    "\n",
    "        Clarity & Redundancy: Identify requirements that are vague, unclear, or redundant.\n",
    "\n",
    "        Missing Elements: Highlight important elements from the description that were not translated into requirements.\n",
    "\n",
    "        Suggestions: Recommend improvements or additional requirements that better align with the description.\n",
    "\n",
    "        Answer in the following format:\n",
    "        \n",
    "        \"understood\": true only if the requirements are complete and accurate,\n",
    "        \"feedback\": Instructions to the generator to improve the requirements.\n",
    "        \n",
    "        Here's the software description:\n",
    "        {description}\n",
    "\n",
    "        Here's the requirements:\n",
    "        {requirements}\n",
    "\n",
    "    \"\"\"\n",
    "\n",
    "    validator_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": validator_prompt}], response_format=StandardSchema)\n",
    "    validator_response = validator_response.choices[0].message.parsed\n",
    "    return validator_response\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function is used to generate a rerun prompt for the requirements generator.\n",
    "# It receives the description, the requirements and the feedback.\n",
    "# It returns the rerun prompt.\n",
    "\n",
    "def generate_rerun_requirements_prompt(description, requirements, feedback):\n",
    "    return f\"\"\"\n",
    "    You are a software analyst. Based on the following software description, you generated the following list of functional and non-functional requirements. \n",
    "    However, the requirements validator rejected the list, with the following feedback. Please review the feedback and improve the list of requirements.\n",
    "\n",
    "    ## Here's the description:\n",
    "    {description}\n",
    "\n",
    "    ## Here's the requirements:\n",
    "    {requirements}\n",
    "\n",
    "    ## Here's the feedback:\n",
    "    {feedback}\n",
    "    \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function generates the requirements based on the description.\n",
    "def generate_requirements(description):\n",
    "    generator_prompt = f\"\"\"\n",
    "    You are a software analyst. Based on the following software description, generate a comprehensive list of both functional and non-functional requirements.\n",
    "\n",
    "    The requirements must be clear, actionable, and written in concise natural language.\n",
    "\n",
    "    Each requirement should describe exactly what the system must do or how it should behave, with enough detail to support MoSCoW prioritization and later transformation into user stories.\n",
    "\n",
    "    Group the requirements into two sections: Functional Requirements and Non-Functional Requirements.\n",
    "\n",
    "    Avoid redundancy. Do not include implementation details unless they are part of the expected behavior.\n",
    "\n",
    "    Write in professional and neutral English.\n",
    "\n",
    "    Output in Markdown format.\n",
    "\n",
    "    Answer in the following format:\n",
    "\n",
    "    \"understood\": true\n",
    "    \"output\": List of requirements\n",
    "\n",
    "    ## Here's the description:\n",
    "    {description}\n",
    "\n",
    "    ## Requirements:\n",
    "    \"\"\"\n",
    "\n",
    "    requirements_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": generator_prompt}], response_format=StandardSchema)\n",
    "    requirements_response = requirements_response.choices[0].message.parsed\n",
    "    requirements = requirements_response.output\n",
    "\n",
    "    requirements_valid = validate_requirements(description, requirements)\n",
    "    \n",
    "    # Validation loop\n",
    "    while not requirements_valid.understood:\n",
    "        rerun_requirements_prompt = generate_rerun_requirements_prompt(description, requirements, requirements_valid.feedback)\n",
    "        requirements_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": rerun_requirements_prompt}], response_format=StandardSchema)\n",
    "        requirements_response = requirements_response.choices[0].message.parsed\n",
    "        requirements = requirements_response.output\n",
    "        requirements_valid = validate_requirements(description, requirements)\n",
    "\n",
    "    return requirements\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function generates the MoSCoW priorization of the requirements.\n",
    "# It receives the requirements.\n",
    "# It returns the MoSCoW priorization.\n",
    "\n",
    "def generate_moscow_priorization(requirements):\n",
    "    priorization_prompt = f\"\"\"\n",
    "    You are a product analyst.\n",
    "    Based on the following list of functional and non-functional requirements, classify each requirement into one of the following MoSCoW categories:\n",
    "\n",
    "    Must Have: Essential requirements that the system cannot function without.\n",
    "\n",
    "    Should Have: Important requirements that add significant value but are not absolutely critical.\n",
    "\n",
    "    Could Have: Desirable but non-essential features, often considered nice-to-have.\n",
    "\n",
    "    Won’t Have (for now): Requirements that are out of scope for the current version but may be included in the future.\n",
    "\n",
    "    For each requirement, place it under the appropriate category and include a brief justification (1–2 sentences) explaining your reasoning.\n",
    "\n",
    "    Format your output using Markdown, like this:\n",
    "\n",
    "    ## Must Have\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Should Have\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Could Have\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Won’t Have (for now)\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Here's the requirements:\n",
    "    {requirements}\n",
    "    \"\"\"\n",
    "\n",
    "    priorization_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": priorization_prompt}], response_format=StandardSchema)\n",
    "    priorization_response = priorization_response.choices[0].message.parsed\n",
    "    priorization = priorization_response.output\n",
    "    return priorization\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "\n",
    "    validation =validate_and_feedback(messages)\n",
    "\n",
    "    if not validation.understood:\n",
    "        print('retornando el feedback')\n",
    "        return validation.feedback\n",
    "    else:\n",
    "        requirements = generate_requirements(validation.output)\n",
    "        moscow_prioritization = generate_moscow_priorization(requirements)\n",
    "        return moscow_prioritization\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gr.ChatInterface(chat, type=\"messages\").launch()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
